﻿Imports System
Imports System.Collections.Generic
Imports System.Diagnostics
Imports System.Globalization
Imports System.Transactions
Imports System.Workflow.ComponentModel

Namespace System.Workflow.Runtime
    Friend Class Scheduler
        ' Methods
        Public Sub New(ByVal rootExec As WorkflowExecutor, ByVal canRun As Boolean)
            Me.rootWorkflowExecutor = rootExec
            Me.threadRequested = False
            Me.canRun = canRun
            Me.highPriorityEntriesQueue = DirectCast(rootExec.RootActivity.GetValue(Scheduler.HighPriorityEntriesQueueProperty), Queue([Of] SchedulableItem))
            Me.normalPriorityEntriesQueue = DirectCast(rootExec.RootActivity.GetValue(Scheduler.NormalPriorityEntriesQueueProperty), Queue([Of] SchedulableItem))
            If (Me.highPriorityEntriesQueue Is Nothing) Then
                Me.highPriorityEntriesQueue = New Queue([Of] SchedulableItem)
                rootExec.RootActivity.SetValue(Scheduler.HighPriorityEntriesQueueProperty, Me.highPriorityEntriesQueue)
            End If
            If (Me.normalPriorityEntriesQueue Is Nothing) Then
                Me.normalPriorityEntriesQueue = New Queue([Of] SchedulableItem)
                rootExec.RootActivity.SetValue(Scheduler.NormalPriorityEntriesQueueProperty, Me.normalPriorityEntriesQueue)
            End If
            Me.empty = ((Me.normalPriorityEntriesQueue.Count = 0) AndAlso (Me.highPriorityEntriesQueue.Count = 0))
        End Sub

        Private Function GetItemToRun() As SchedulableItem
            Dim item As SchedulableItem = Nothing
            SyncLock Me.syncObject
                Dim flag As Boolean = False
                If ((Me.highPriorityEntriesQueue.Count > 0) OrElse (Me.normalPriorityEntriesQueue.Count > 0)) Then
                    flag = True
                    If Me.AbortOrTerminateRequested Then
                        item = Nothing
                    ElseIf (Me.highPriorityEntriesQueue.Count > 0) Then
                        item = Me.highPriorityEntriesQueue.Dequeue
                    ElseIf Me.CanRun Then
                        If ((DirectCast(Me.RootWorkflowExecutor, IWorkflowCoreRuntime).CurrentAtomicActivity Is Nothing) AndAlso (Me.normalPriorityEntriesQueue.Count > 0)) Then
                            item = Me.normalPriorityEntriesQueue.Dequeue
                        End If
                    Else
                        item = Nothing
                    End If
                End If
                If Not flag Then
                    Me.empty = True
                End If
                Me.threadRequested = (Not item Is Nothing)
            End SyncLock
            Return item
        End Function

        Public Sub PostPersist()
            Me.transactedEntries = Nothing
        End Sub

        Public Sub [Resume]()
            Me.canRun = True
            If Not Me.empty Then
                Me.RootWorkflowExecutor.ScheduleForWork
            End If
        End Sub

        Public Sub ResumeIfRunnable()
            If (Me.canRun AndAlso Not Me.empty) Then
                Me.RootWorkflowExecutor.ScheduleForWork
            End If
        End Sub

        Public Sub Rollback()
            If ((Not Me.transactedEntries Is Nothing) AndAlso (Me.transactedEntries.Count > 0)) Then
                Dim enumerator As IEnumerator([Of] SchedulableItem) = Me.normalPriorityEntriesQueue.GetEnumerator
                Dim queue As New Queue([Of] SchedulableItem)
                Do While enumerator.MoveNext
                    If Not Me.transactedEntries.Contains(enumerator.Current) Then
                        queue.Enqueue(enumerator.Current)
                    End If
                Loop
                Me.normalPriorityEntriesQueue.Clear
                enumerator = queue.GetEnumerator
                Do While enumerator.MoveNext
                    Me.normalPriorityEntriesQueue.Enqueue(enumerator.Current)
                Loop
                Me.transactedEntries = Nothing
            End If
        End Sub

        Public Sub Run()
        Label_0000:
            Me.RootWorkflowExecutor.ProcessQueuedEvents
            Dim itemToRun As SchedulableItem = Me.GetItemToRun
            Dim flag As Boolean = False
            If (Not itemToRun Is Nothing) Then
                Dim currentActivity As Activity = Nothing
                Dim exp As Exception = Nothing
                Dim transactionalProperties As TransactionalProperties = Nothing
                Dim contextId As Integer = itemToRun.ContextId
                Dim contextActivityForId As Activity = Me.RootWorkflowExecutor.GetContextActivityForId(contextId)
                If (contextActivityForId Is Nothing) Then
                    Throw New InvalidOperationException(ExecutionStringManager.InvalidExecutionContext)
                End If
                currentActivity = contextActivityForId.GetActivityByName(itemToRun.ActivityId)
                [Using] New ServiceEnvironment(currentActivity)
                    exp = Nothing
                    Dim flag2 As Boolean = False
                    Try 
                        If (currentActivity Is Nothing) Then
                            Throw New InvalidOperationException(String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.InvalidActivityName, New Object() { itemToRun.ActivityId }))
                        End If
                        Dim atomicActivity As Activity = Nothing
                        If Me.RootWorkflowExecutor.IsActivityInAtomicContext(currentActivity, atomicActivity) Then
                            transactionalProperties = DirectCast(atomicActivity.GetValue(WorkflowExecutor.TransactionalPropertiesProperty), TransactionalProperties)
                            If (Not WorkflowExecutor.CheckAndProcessTransactionAborted(transactionalProperties) AndAlso (transactionalProperties.TransactionScope Is Nothing)) Then
                                transactionalProperties.TransactionScope = New TransactionScope(transactionalProperties.Transaction, TimeSpan.Zero, EnterpriseServicesInteropOption.Full)
                                WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, ("Workflow Runtime: Scheduler: instanceId: " & Me.RootWorkflowExecutor.InstanceIdString & "Entered into TransactionScope, Current atomic acitivity " & atomicActivity.Name))
                            End If
                        End If
                        flag = True
                        WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 1, "Workflow Runtime: Scheduler: InstanceId: {0} : Running scheduled entry: {1}", New Object() { Me.RootWorkflowExecutor.InstanceIdString, itemToRun.ToString })
                        Me.RootWorkflowExecutor.stateChangedSincePersistence = True
                        itemToRun.Run(Me.RootWorkflowExecutor)
                    Catch exception2 As Exception
                        If WorkflowExecutor.IsIrrecoverableException(exception2) Then
                            flag2 = True
                            Throw
                        End If
                        If (Not transactionalProperties Is Nothing) Then
                            transactionalProperties.TransactionState = TransactionProcessState.AbortProcessed
                        End If
                        exp = exception2
                    Finally
                        If Not flag2 Then
                            If flag Then
                                WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 1, "Workflow Runtime: Scheduler: InstanceId: {0} : Done with running scheduled entry: {1}", New Object() { Me.RootWorkflowExecutor.InstanceIdString, itemToRun.ToString })
                            End If
                            If (Not exp Is Nothing) Then
                                Me.RootWorkflowExecutor.ExceptionOccured(exp, If((currentActivity Is Nothing), contextActivityForId, currentActivity), Nothing)
                                exp = Nothing
                            End If
                        End If
                    End Try
                    goto Label_0000
                End [Using]
            End If
        End Sub

        Public Sub ScheduleItem(ByVal s As SchedulableItem, ByVal isInAtomicTransaction As Boolean, ByVal transacted As Boolean)
            SyncLock Me.syncObject
                WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 1, "Workflow Runtime: Scheduler: InstanceId: {0} : Scheduling entry: {1}", New Object() { Me.RootWorkflowExecutor.InstanceIdString, s.ToString })
                If(isInAtomicTransaction, Me.highPriorityEntriesQueue, Me.normalPriorityEntriesQueue).Enqueue(s)
                If transacted Then
                    If (Me.transactedEntries Is Nothing) Then
                        Me.transactedEntries = New Queue([Of] SchedulableItem)
                    End If
                    Me.transactedEntries.Enqueue(s)
                End If
                If (Not Me.threadRequested AndAlso Me.CanRun) Then
                    Me.RootWorkflowExecutor.ScheduleForWork
                    Me.threadRequested = True
                End If
                Me.empty = False
            End SyncLock
        End Sub

        Public Overrides Function ToString() As String
            Return ("Scheduler('" & Me.RootWorkflowExecutor.WorkflowDefinition.QualifiedName & "')")
        End Function


        ' Properties
        Friend Property AbortOrTerminateRequested As Boolean
            Get
                Return Me.abortOrTerminateRequested
            End Get
            Set(ByVal value As Boolean)
                Me.abortOrTerminateRequested = value
            End Set
        End Property

        Public Property CanRun As Boolean
            Get
                Return Me.canRun
            End Get
            Set(ByVal value As Boolean)
                Me.canRun = value
            End Set
        End Property

        Public ReadOnly Property IsStalledNow As Boolean
            Get
                Return Me.empty
            End Get
        End Property

        Protected ReadOnly Property RootWorkflowExecutor As WorkflowExecutor
            Get
                Return Me.rootWorkflowExecutor
            End Get
        End Property


        ' Fields
        Private abortOrTerminateRequested As Boolean
        Private canRun As Boolean
        Private empty As Boolean
        Private highPriorityEntriesQueue As Queue([Of] SchedulableItem)
        Friend Shared HighPriorityEntriesQueueProperty As DependencyProperty = DependencyProperty.RegisterAttached("HighPriorityEntriesQueue", GetType(Queue([Of] SchedulableItem)), GetType(Scheduler))
        Private normalPriorityEntriesQueue As Queue([Of] SchedulableItem)
        Friend Shared NormalPriorityEntriesQueueProperty As DependencyProperty = DependencyProperty.RegisterAttached("NormalPriorityEntriesQueue", GetType(Queue([Of] SchedulableItem)), GetType(Scheduler))
        Private rootWorkflowExecutor As WorkflowExecutor
        Private syncObject As Object = New Object
        Private threadRequested As Boolean
        Private transactedEntries As Queue([Of] SchedulableItem)
    End Class
End Namespace

